import { mat3, vec2 } from "gl-matrix";
import { DEFAULT_SCALE2, DEFAULT_VALUE2, Value2 } from "./Graph";

/**
 * 2D矩阵
 */
export class Matrix3 {
  /** mat3 */
  private value: mat3;
  /** 分解数据脏标志 */
  private _isDecomposeDirty: boolean;
  /** 分解数据 */
  private _decomposeData: {
    translation: Value2;
    rotation: number;
    scaling: Value2;
  };

  constructor(mat3Value?: mat3) {
    this.value = mat3Value ?? mat3.create();
    this._isDecomposeDirty = true;
    this._decomposeData = {
      translation: DEFAULT_VALUE2,
      rotation: 0,
      scaling: DEFAULT_SCALE2
    };
  }

  /**
   * 创建一个矩阵
   *
   * @param position
   * @param rotation
   * @param scale
   */
  public static create(
    position: Value2 = DEFAULT_VALUE2,
    rotation: number = 0,
    scale: Value2 = DEFAULT_SCALE2
  ) {
    const matrix3 = new Matrix3();
    matrix3.translate(position);
    matrix3.rotate(rotation);
    matrix3.scale(scale);
    return matrix3;
  }

  /**
   * 设置矩阵 列主序
   *
   * @param value
   */
  public setValue(value: mat3) {
    this.value = value;
    this.dirtyDecompose();
  }

  /**
   * 标记分解矩阵为脏
   */
  private dirtyDecompose() {
    this._isDecomposeDirty = true;
  }

  /**
   * 平移
   *
   * @param position
   */
  public translate(position: Value2): Matrix3 {
    const decalCoordVec2 = vec2.fromValues(position.x, position.y);
    mat3.translate(this.value, this.value, decalCoordVec2);
    this.dirtyDecompose();
    return this;
  }

  /**
   * 旋转
   *
   * @param rotation
   */
  public rotate(rotation: number): Matrix3 {
    mat3.rotate(this.value, this.value, rotation);
    this.dirtyDecompose();
    return this;
  }

  /**
   * 缩放
   *
   * @param scale
   */
  public scale(scale: Value2): Matrix3 {
    const decalScaleVec2 = vec2.fromValues(scale.x, scale.y);
    mat3.scale(this.value, this.value, decalScaleVec2);
    this.dirtyDecompose();
    return this;
  }

  /**
   * 获取逆矩阵
   *
   * @returns
   */
  public static getInverse(matrix: Matrix3): Matrix3 {
    const inverse = new Matrix3();
    mat3.invert(inverse.value, matrix.value);
    return inverse;
  }

  /**
   * 获取逆矩阵
   *
   * @returns
   */
  public getInverse() {
    return Matrix3.getInverse(this);
  }

  /**
   * 转为逆矩阵
   */
  public invert(): Matrix3 {
    mat3.invert(this.value, this.value);
    this.dirtyDecompose();
    return this;
  }

  /**
   * 乘以一个点
   *
   * @param vec
   * @returns
   */
  public multiplyPoint(vec: Value2): Value2 {
    const decalCoordVec2 = vec2.fromValues(vec.x, vec.y);
    const result = vec2.create();
    vec2.transformMat3(result, decalCoordVec2, this.value);
    return { x: result[0], y: result[1] };
  }

  public static multiply(matrix1: Matrix3, matrix2: Matrix3): Matrix3 {
    const result = new Matrix3();
    mat3.multiply(result.value, matrix1.value, matrix2.value);
    return result;
  }

  /**
   * 左乘以一个矩阵
   */
  public multiply(matrix: Matrix3) {
    const result = new Matrix3();
    mat3.multiply(result.value, this.value, matrix.value);
    return result;
  }

  /**
   * 右乘以一个矩阵
   */
  public premultiply(matrix: Matrix3) {
    const result = new Matrix3();
    mat3.multiply(result.value, matrix.value, this.value);
    return result;
  }

  /**
   * 分解矩阵
   *
   * @returns
   */
  private decompose(): void {
    if (!this._isDecomposeDirty) {
      return;
    }

    const m = this.value;
    // 1) 平移
    this._decomposeData.translation.x = m[6];
    this._decomposeData.translation.y = m[7];

    // 2) 计算缩放：scaleX, scaleY
    let scaleX = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
    let scaleY = Math.sqrt(m[3] * m[3] + m[4] * m[4]);

    // 3) 检查行列式，以识别翻转
    //    若 det<0，代表某个轴缩放是负的(镜像/翻转)
    const det = m[0] * m[4] - m[1] * m[3];
    if (det < 0) {
      // 通常做法：把 Y 轴的符号翻转，以保持旋转方向一致
      scaleY = -scaleY;
    }

    this._decomposeData.scaling.x = scaleX;
    this._decomposeData.scaling.y = scaleY;

    // 4) 计算旋转
    //    只要没倾斜(shear)，atan2(m[1], m[0]) 能得到正确角度
    const rotation = Math.atan2(m[1], m[0]);
    this._decomposeData.rotation = rotation;

    // 标记分解完成
    this._isDecomposeDirty = false;
  }

  /**
   * 获取平移
   */
  public get translation(): Value2 {
    this.decompose();
    return this._decomposeData.translation;
  }

  /**
   * 获取旋转
   */
  public get rotation(): number {
    this.decompose();
    return this._decomposeData.rotation;
  }

  /**
   * 获取缩放
   */
  public get scaling(): Value2 {
    this.decompose();
    return this._decomposeData.scaling;
  }

  /**
   * 获取变换数据
   */
  public get transformData() {
    this.decompose();
    return this._decomposeData;
  }
}
